package com.example.exam.shiro;

import com.example.exam.sysResource.service.SysResourceService;
import com.example.exam.sysRole.service.SysRoleService;
import com.example.exam.user.model.UserBean;
import com.example.exam.user.service.UserRoleService;
import com.example.exam.user.service.UserService;
import com.example.exam.utils.MD5;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 自定义realm
 * @author MuLon
 * @date 2018年12月4日
 * @note
 */
public class ShiroRealm extends AuthorizingRealm {

	private static Logger log = LoggerFactory.getLogger(ShiroRealm.class);

	@Autowired
	private UserService userService;
	@Autowired
	private SysRoleService roleService;
	@Autowired
	private SysResourceService sysResourceService;
	@Autowired
	private UserRoleService userRoleService;

	/**
	 * 构造函数，设置安全的初始化信息
	 */
	public ShiroRealm() {
		super();
		setAuthenticationTokenClass(UsernamePasswordToken.class);
		// 设置密码认证的加密方法
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(UserBean.HASH_ALGORITHM);
		matcher.setHashIterations(UserBean.HASH_INTERATIONS);
		setCredentialsMatcher(matcher);
	}

	/**
	 * 获取当前认证实体的授权信息（授权包括：角色、权限）
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		log.info("访问控制，开始鉴权。。。。。。");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		// 获取当前登录的用户名
		ShiroPrincipal subject = (ShiroPrincipal) super.getAvailablePrincipal(principals);
		int userId = subject.getUserId();
		String userName = subject.getUserName();
		try {
			if (!subject.isAuthorized()) {
				log.info("实体认证");
				// 管理员用户登陆，根据用户名称，获取该用户所有的权限列表
				List<String> roleList = roleService.getUserRolesName(userId);
				List<String> authorities = userRoleService.authorization(userId);
				if (roleList == null) {
					roleList = new ArrayList<>();
				}
				subject.setRoles(roleList);
				subject.setAuthorities(authorities);
				subject.setAuthorized(true);
				log.info("用户【" + userName + "】授权初始化成功......");
				log.info("用户【" + userName + "】 角色列表为：" + subject.getRoles());
				log.info("用户【" + userName + "】 权限列表为：" + subject.getAuthorities());
			}
		} catch (Exception e) {
			throw new AuthorizationException("用户【" + userName + "】授权失败", e);
		}
		// 给当前用户设置权限
		if (subject.getAuthorities()!=null||subject.getRoles()!=null){
			info.addStringPermissions(subject.getAuthorities());
			info.addRoles(subject.getRoles());
		}
		return info;
	}

	/**
	 * 根据认证方式（如表单）获取用户名称、密码
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
			throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String accountName = token.getUsername();
		if (StringUtils.isEmpty(accountName)) {
			throw new UnknownAccountException("用户名不可为空！");
		}
		UserBean user = null;
		try {
			user = userService.selectId(accountName);
			user.setToken(MD5.getMD5ofStr(System.currentTimeMillis() + ""));
		} catch (Exception e) {
			log.warn("获取用户失败\n" + e.getMessage());
			e.printStackTrace();
		}
		if (null == user) {
			throw new UnknownAccountException("用户名或密码错误！");
		}
		if (user.getStatus() != 1) {
			throw new LockedAccountException("用户被禁止使用！");
		}
		user.setLogin_time(new Date());
		userService.updateLoginUser(user);
		byte[] salt = EncodeUtils.hexDecode(user.getSalt());
		ShiroPrincipal principal = new ShiroPrincipal(user);
		return new SimpleAuthenticationInfo(principal, user.getPassword(), ByteSource.Util.bytes(salt), getName());
	}

	/**
	 * 更新用户授权信息缓存.
	 *
	 * @param principal 用户标志
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
		super.clearCachedAuthorizationInfo(principals);
	}

	/**
	 * 清除所有用户授权信息缓存
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}
}
